﻿
namespace GMap.NET.WindowsPresentation
{
   using System;
   using System.Runtime.InteropServices;

   /// <summary>
   /// This class implements a high precision timer using the Win32 QueryPerformanceCounter API.
   /// Typical usage:
   /// <code>
   ///     PerfTimer t = new PerfTimer();
   ///     t.Start();
   ///     ...
   ///     t.Stop();
   ///     long ms = t.GetDuration();
   /// </code>
   /// You can also use it to add up a bunch of times in a loop and report average, mininum
   /// and maximum times.
   /// </summary>
   public class PerfTimer
   {
      long _start;
      long _end;
      long _freq;
      long _min;
      long _max;
      long _count;
      long _sum;

      /// <summary>
      /// 
      /// </summary>
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806")]
      public PerfTimer()
      {
         _start = _end = 0;
         QueryPerformanceFrequency(ref _freq);
         _min = _max = _count = _sum = 0;
      }

      /// <summary>
      /// Set current time as the start time.
      /// </summary>
      public void Start()
      {
         _start = GetCurrentTime();
         _end = _start;
      }

      /// <summary>
      /// Set the current time as the end time.
      /// </summary>
      public void Stop()
      {
         _end = GetCurrentTime();
      }

      /// <summary>
      /// Get the time in milliseconds between Start() and Stop().
      /// </summary>
      /// <returns>Milliseconds</returns>
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024")]
      public long GetDuration()
      { // in milliseconds.            
         return GetMilliseconds(GetDurationInTicks());
      }

      /// <summary>
      /// Convert the given argument from "ticks" to milliseconds.
      /// </summary>
      /// <param name="ticks">Number of ticks returned from GetTicks()</param>
      /// <returns>Milliseconds</returns>
      public long GetMilliseconds(long ticks)
      {
         return (ticks * (long) 1000) / _freq;
      }

      /// <summary>
      /// Get the time between Start() and Stop() in the highest fidelity possible
      /// as defined by Windows QueryPerformanceFrequency.  Usually this is nanoseconds.
      /// </summary>
      /// <returns>High fidelity tick count</returns>
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024")]
      public long GetDurationInTicks()
      { // in nanoseconds.
         return (_end - _start);
      }

      /// <summary>
      /// Get current time in ighest fidelity possible as defined by Windows QueryPerformanceCounter.  
      /// Usually this is nanoseconds.
      /// </summary>
      /// <returns>High fidelity tick count</returns>
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806")]
      [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024")]
      public static long GetCurrentTime()
      { // in nanoseconds.
         long i = 0;
         QueryPerformanceCounter(ref i);
         return i;
      }

      // These methods allow you to count up multiple iterations and
      // then get the median, average and percent variation.

      /// <summary>
      /// Add the given time to a running total so we can compute minimum, maximum and average.
      /// </summary>
      /// <param name="time">The time to record</param>
      public void Count(long time)
      {
         if(_min == 0)
            _min = time;
         if(time < _min)
            _min = time;
         if(time > _max)
            _max = time;
         _sum += time;
         _count++;
      }

      /// <summary>
      /// Return the minimum time recorded by the Count() method since the last Clear
      /// </summary>
      /// <returns>The minimum value</returns>
      public long Minimum()
      {
         return _min;
      }

      /// <summary>
      /// Return the maximum time recorded by the Count() method since the last Clear
      /// </summary>
      /// <returns>The maximum value</returns>
      public long Max()
      {
         return _max;
      }

      /// <summary>
      /// Return the median of the values recorded by the Count() method since the last Clear
      /// </summary>
      /// <returns>The median value</returns>
      public double Median()
      {
         return (_min + ((_max - _min) / 2.0));
      }

      /// <summary>
      /// Return the variance in the numbers recorded by the Count() method since the last Clear
      /// </summary>
      /// <returns>Percentage between 0 and 100</returns>
      public double PercentError()
      {
         double spread = (_max - _min) / 2.0;
         double percent = ((double) (spread * 100.0) / (double) (_min));
         return percent;
      }

      /// <summary>
      /// Return the avergae of the values recorded by the Count() method since the last Clear
      /// </summary>
      /// <returns>The average value</returns>
      public long Average()
      {
         if(_count == 0)
            return 0;
         return _sum / _count;
      }

      /// <summary>
      /// Reset the timer to its initial state.
      /// </summary>
      public void Clear()
      {
         _start = _end = _min = _max = _sum = _count = 0;
      }

      [DllImport("KERNEL32.DLL", EntryPoint = "QueryPerformanceCounter", SetLastError = true,
                  CharSet = CharSet.Unicode, ExactSpelling = true,
                  CallingConvention = CallingConvention.StdCall)]
      static extern int QueryPerformanceCounter(ref long time);

      [DllImport("KERNEL32.DLL", EntryPoint = "QueryPerformanceFrequency", SetLastError = true,
           CharSet = CharSet.Unicode, ExactSpelling = true,
           CallingConvention = CallingConvention.StdCall)]
      static extern int QueryPerformanceFrequency(ref long freq);
   }
}